package com.lan.parse;

import com.lan.ScriptContext;
import com.lan.antlr.ScriptBaseListener;
import com.lan.antlr.ScriptParser;
import com.lan.model.symbol.*;
import org.antlr.v4.runtime.ParserRuleContext;

import java.util.Stack;

public class TypeAndScopeScanner extends ScriptBaseListener {

    private ScriptContext context;

    private Stack<Scope> scopeStack = new Stack<>();

    public TypeAndScopeScanner(ScriptContext context) {
        this.context = context;
    }

    private void print(ParserRuleContext ctx, String name) {
//        System.out.println(ctx.getRuleIndex() + " :   " + ctx.toString() + "   " + (StringUtils.isNotBlank(name) ? name : "") + " " + (StringUtils.isNotBlank(ctx.getText()) ? ctx.getText() : ""));
    }

    private Scope pushScope(Scope scope, ParserRuleContext ctx) {
        context.nodeOfScope(ctx, scope);
        scope.setCtx(ctx);
        scopeStack.push(scope);
        return scope;
    }

    /**
     * 从栈中弹出scope
     */
    private void popScope() {
        scopeStack.pop();
    }

    /**
     * 在遍历树的过程中，当前的Scope
     *
     * @return
     */
    private Scope scope() {
        if (scopeStack.size() > 0) {
            return scopeStack.peek();
        } else {
            return null;
        }
    }


    @Override
    public void enterProg(ScriptParser.ProgContext ctx) {
        NameSpace scope = new NameSpace("root", scope(), ctx);
        context.setNameSpace(scope);
        pushScope(scope, ctx);
        print(ctx, "enterProg");
    }


    @Override
    public void exitProg(ScriptParser.ProgContext ctx) {
        popScope();
        print(ctx, "exitProg");
    }


    @Override
    public void enterBlock(ScriptParser.BlockContext ctx) {
        //对于函数，不需要再额外建一个scope
        if ((ctx.parent instanceof ScriptParser.FunctionBodyContext)) {
            Block scope = new Block(scope(), ctx);
            scope().addSymbol(scope);
            pushScope(scope, ctx);
            print(ctx, "enterBlock");
        }
    }

    @Override
    public void exitBlock(ScriptParser.BlockContext ctx) {
        if ((ctx.parent instanceof ScriptParser.FunctionBodyContext)) {
            popScope();
            print(ctx, "exitBlock");
        }
    }


    @Override
    public void enterFunctionDeclaration(ScriptParser.FunctionDeclarationContext ctx) {
        String name = ctx.name().getText();
        //注意：目前function的信息并不完整，参数要等到TypeResolver.java中去确定。
        Function function = new Function(name, scope(), ctx);
//        context.type(function);
        scope().addSymbol(function);

        // 创建一个新的scope
        pushScope(function, ctx);
        print(ctx, "enterFunctionDeclaration");

    }

    @Override
    public void exitFunctionDeclaration(ScriptParser.FunctionDeclarationContext ctx) {
        popScope();
        print(ctx, "exitFunctionDeclaration");
    }


    @Override
    public void enterClassDeclaration(ScriptParser.ClassDeclarationContext ctx) {
        // 把类的签名存到符号表中，不能跟已有符号名称冲突
        String name = ctx.name().getText();
        Clazz clazz = new Clazz(name, ctx);
//        context.type(clazz);
        if (context.lookupClass(scope(), name) != null) {
            context.log("duplicate class name:" + name, ctx); // 只是报警，但仍然继续解析
        }
        scope().addSymbol(clazz);
        pushScope(clazz, ctx);
        print(ctx, "enterClassDeclaration");

    }

    @Override
    public void exitClassDeclaration(ScriptParser.ClassDeclarationContext ctx) {
        popScope();
        print(ctx, "exitClassDeclaration");
    }


    @Override
    public void enterStatement(ScriptParser.StatementContext ctx) {
        //为for建立额外的Scope
        if (ctx.FOR() != null) {
            Block scope = new Block(scope(), ctx);
            scope().addSymbol(scope);
            pushScope(scope, ctx);
            print(ctx, "enterStatement");
        }
    }

    @Override
    public void exitStatement(ScriptParser.StatementContext ctx) {
        //释放for语句的外层scope
        if (ctx.FOR() != null) {
            popScope();
            print(ctx, "exitStatement");
        }
    }
}























